#pragma once
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <string>
#include <memory>
#include <iostream>
#include <errno.h>
#include <sys/wait.h>

using namespace std;

//这里就只搞二元信号量集了

#define perm 0666
string path(".");
int PROJ_ID = 0x7866;

#define default_nsems 1

#define GET_SEM IPC_CREAT
#define CREATE_SEM (IPC_CREAT | IPC_EXCL | perm)

class Semaphore{
    void PV(int opr){
        //因为当前信号量集二元，所以内部只有一个信号量，编号为0
        struct sembuf _sem_buf;
        _sem_buf.sem_flg = 0;
        _sem_buf.sem_op = opr;
        _sem_buf.sem_num = 0;

        int semop_rd = semop(_semid, &_sem_buf, 1);
        if(semop_rd < 0){
            cerr << "semop error!" << endl;
            exit(errno);
        }
    }
public:
    Semaphore(int semid)
        :_semid(semid)
    {}

    void P(){
        PV(-1);
    }

    void V(){
        PV(1);
    }
private:
    int _semid;
};

//创建信号量集
class BulidSemphore{
public:
    BulidSemphore(int flag, int nems = default_nsems)
        :_nsems(default_nsems), _flag(flag)
    {
        _key = ftok(path.c_str(), PROJ_ID);
        if(_key < 0){
            cerr << "ftok error!" << endl;
            exit(errno);
        }
        else cout << "ftok seccess! key : " << _key << endl;
    }

    ~BulidSemphore(){
        if(_flag == GET_SEM) return;
        int semctl_rd = semctl(_semid, 0, IPC_RMID);
        if(semctl_rd < 0){
            cerr << "remove sem error!" << endl;
            exit(errno);
        }
        else cout << "remove sem success!" << endl;
    }

    shared_ptr<Semaphore> bulid(){
         //进行信号量集的创建
        _semid = semget(_key, _nsems, _flag);
        if(_semid < 0){
            cerr << "semget error!" << endl;
            exit(errno);
        }
        else cout << "semget success!" << endl;
        return make_shared<Semaphore>(_semid);
    } 

    void Init(){
        //对创建好的信号量集进行初始化工作 -> semctl
        if(_flag == GET_SEM) return;

        //这个结构体需要自己写
        union semun {
            int              val;    /* Value for SETVAL val > 0*/
            struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
            unsigned short  *array;  /* Array for GETALL, SETALL */
            struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
        }un;
        un.val = 1;
        int _semctl_rd = semctl(_semid, 0, SETVAL, un);
        if(_semctl_rd < 0){
            cerr << "sem Init(SETVAL) error!" << endl;
            exit(errno);
        }
        else cout << "sem Init(Setval) success!" << endl;
    }

private:
    key_t _key;
    int _nsems;
    int _flag;
    int _semid;
};